#pragma once
#include"session.hpp"
#include<condition_variable>
#include<list>

class match_queue
{
public:
    int size()
    {
        std::unique_lock<std::mutex> ul(_mutex);
        return _list.size();
    }

    bool empty()
    {
        std::unique_lock<std::mutex> ul(_mutex);
        return _list.empty();
    }

    void wait()
    {
        std::unique_lock<std::mutex> ul(_mutex);
        _cond.wait(ul);
    }

    void push(int uid)
    {   
        std::unique_lock<std::mutex> ul(_mutex);
        _list.emplace_back(uid);
        _cond.notify_all();
    }

    bool pop(int& uid)
    {
        if(empty())
        {
            return false;
        }
        std::unique_lock<std::mutex> ul(_mutex);
        uid =  _list.front();
        _list.pop_front();
        return true;
    }

    void remove(int uid)
    {
        std::unique_lock<std::mutex> ul(_mutex);
        _list.remove(uid);
    }

private:
    std::mutex _mutex;
    std::condition_variable _cond;
    std::list<int> _list;
};

class match
{
public:
    match(user_table* table,room_manager* rm,online_manager* online)
    :_tlow(&match::t_entry_low,this)
    ,_tmiddle(&match::t_entry_middle,this)
    ,_thigh(&match::t_entry_high,this)
    ,_table(table)
    ,_rm(rm)
    ,_online(online)
    {

    }

    ~match()
    {

    }

    void t_entry_low()
    {
        handle_mq(_mqlow);
    } 
    
    void t_entry_middle()
    {
        handle_mq(_mqmiddle);
    } 
    
    void t_entry_high()
    {
        handle_mq(_mqhigh);
    }

    void handle_mq(match_queue& mq)
    {
        while(true)
        {
            while(mq.size() < 2)
            {
                mq.wait();
            }
            int uid1,uid2;
            mq.pop(uid1);
            mq.pop(uid2);
            wsserver_t::connection_ptr conn1 = _online->get_conn_from_hall(uid1);
            if(conn1.get() == nullptr)
            {
                mq.push(uid2);
                continue;
            }
            wsserver_t::connection_ptr conn2 = _online->get_conn_from_hall(uid2);
            if(conn2.get() == nullptr)
            {
                mq.push(uid1);
                continue;
            }
            _rm->create_room(uid1,uid2);
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            json_util::serialize(resp,body);
            conn1->send(body);
            conn2->send(body);
        }
    }

    void add(int uid)
    {
        Json::Value user;
        _table->select_by_id(uid,user);
        int score = user["score"].asInt();
        if(score < 2000)
        {
            _mqlow.remove(uid);
            _mqlow.push(uid);
        }
        else if(score >= 2000 && score < 3000)
        {
            _mqmiddle.remove(uid);
            _mqmiddle.push(uid);
        }   
        else //score > = 3000
        {
            _mqhigh.remove(uid);
            _mqhigh.push(uid);
        }
    }

    void del(int uid)
    {
        Json::Value user;
        _table->select_by_id(uid,user);
        int score = user["score"].asInt();
        if(score < 2000)
        {
            _mqlow.remove(uid);
        }
        else if(score >= 2000 && score < 3000)
        {
            _mqmiddle.remove(uid);
        }   
        else //score > = 3000
        {
            _mqhigh.remove(uid);
        }
    }

private:
    match_queue _mqlow; 
    match_queue _mqmiddle; 
    match_queue _mqhigh; 
    std::thread _tlow;
    std::thread _tmiddle;
    std::thread _thigh;
    user_table* _table;
    room_manager* _rm;
    online_manager* _online;
};